﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using moose.irc;
using moose.utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace moose.extension {

    /// <summary>
    /// Manages MooseIRC Extensions
    /// </summary>
    internal static class ExtensionManager {

        /// <summary>
        /// Collection of fully instantiated commands (overrides and all)
        /// </summary>
        private static Dictionary<string, IrcAction> m_dictCommands;

        /// <summary>
        /// Collection of fully instantiated responses (overrides and all)
        /// </summary>
        private static Dictionary<string, IrcAction> m_dictResponses;

        /// <summary>
        /// Collection of command types
        /// </summary>
        private static Dictionary<string, Type> m_dictCommandTypes;

        /// <summary>
        /// Collection of response types
        /// </summary>
        private static Dictionary<string, Type> m_dictResponseTypes;

        /// <summary>
        /// Initialize Static Members
        /// </summary>
        static ExtensionManager() {
            m_dictCommands = new Dictionary<string, IrcAction>();
            m_dictCommandTypes = new Dictionary<string, Type>();
            m_dictResponses = new Dictionary<string, IrcAction>();
            m_dictResponseTypes = new Dictionary<string, Type>();

            //Cache all available actions
            cache_all_actons(typeof(CommandAttribute));
            cache_all_actons(typeof(ResponseAttribute));
        }

        /// <summary>
        /// Get Action Cache
        /// </summary>
        /// <param name="action_type">Action Type Classification</param>
        /// <returns>Cache Table</returns>
        private static Dictionary<string, IrcAction> get_cache(Type action_type) {
            if (action_type == typeof(CommandAttribute)) {
                return m_dictCommands;
            } else if (action_type == typeof(ResponseAttribute)) {
                return m_dictResponses;
            } else {
                return null;
            }
        }

        /// <summary>
        /// Get Cache for Action Types
        /// </summary>
        /// <param name="action_type">Action Type Classification</param>
        /// <returns>Type Cache Table</returns>
        private static Dictionary<string, Type> get_type_cache(Type action_type) {
            if (action_type == typeof(CommandAttribute)) {
                return m_dictCommandTypes;
            } else if (action_type == typeof(ResponseAttribute)) {
                return m_dictResponseTypes;
            } else {
                return null;
            }
        }

        /// <summary>
        /// Cache all Action Type References
        /// </summary>
        /// <param name="action_type">this is the action attribute type</param>
        private static void cache_all_actons(Type action_type) {
            object[] attributes;
            TriggerAttribute trigger_attribute;
            string trigger_text;
            Dictionary<string, Type> type_cache = get_type_cache(action_type);
            foreach (Assembly extension in DriverUtility.Extensions) {
                foreach (Type action in extension.GetTypes()) {
                    attributes = action.GetCustomAttributes(action_type, false);
                    if (attributes.Length == 1) { //We have ourselves a command
                        attributes = action.GetCustomAttributes(typeof(TriggerAttribute), false);
                        foreach (object trigger in attributes) {
                            trigger_attribute = (TriggerAttribute)trigger;
                            trigger_text = trigger_attribute.TriggerText;
                            if (type_cache.ContainsKey(trigger_text)) {
                                throw new ExtensionException(action.ToString() + " could not be imported; a trigger by " + trigger_text
                                    + " has already been imported on " + type_cache[trigger_text].ToString());
                            } else {
                                type_cache[trigger_text] = action;
                            }
                        }
                    }
                }
            }
            return;
        }

        /// <summary>
        /// Attempt to obtain an action for a trigger
        /// </summary>
        /// <param name="trigger_text">trigger text to grab the action</param>
        /// <param name="action_type">Action Type Classification</param>
        /// <returns>an IrcAction, or null if no action could be found</returns>
        public static IrcAction get_action(string trigger_text, Type action_type) {
            //If we've already created this, then lets save ourselves some time.
            Dictionary<string, IrcAction> cache = get_cache(action_type);
            trigger_text = trigger_text.ToUpper();
            if (cache.ContainsKey(trigger_text))
                return cache[trigger_text];  //Return Cached Copy

            //Otherwise, we should try and create it, then stick the sucker in cache
            Dictionary<string, Type> type_cache = get_type_cache(action_type);
            if (!type_cache.ContainsKey(trigger_text))
                return null; //If we dont have any handlers for this trigger type, we cant return one

            //If we do have the type, but it has not already been created and cached, we need to do that now.
            Assembly asm = type_cache[trigger_text].Assembly;
            object action = asm.CreateInstance(type_cache[trigger_text].ToString()); //The IrcAction should take care of the rest
            //type_cache.Remove(trigger_text); //We no longer need this type-reference!
            cache[trigger_text] = (IrcAction)action;
            return cache[trigger_text]; //Return newly cached copy
        }

        /// <summary>
        /// Gets Action Type from type cache
        /// </summary>
        /// <param name="trigger_text">trigger text</param>
        /// <param name="action_type">Action Type Classification</param>
        /// <returns>Irc Action Type</returns>
        public static Type get_action_type(string trigger_text, Type action_type) {
            Dictionary<string, Type> type_cache = get_type_cache(action_type);
            if (!type_cache.ContainsKey(trigger_text)) {
                return null;
            } else {
                return type_cache[trigger_text];
            }
        }
    }
}
